From da4eba9f1e914c1bfd16644d7f8cc1f418d84bb5 Mon Sep 17 00:00:00 2001 From: "mjw@wray-m-3.hpl.hp.com" Date: Thu, 1 Jul 2004 14:16:50 +0000 Subject: [PATCH] bitkeeper revision 1.1041.3.2 (40e41cd2iveMByv37PKjiwl04MigLQ) Help for defaults files. --- .rootkeys | 2 +- tools/examples/xmdefaults | 25 ++- tools/python/xen/xend/XendClient.py | 30 +++ tools/python/xen/xend/server/cstruct.py | 269 ------------------------ tools/python/xen/xm/create.py | 49 ++++- tools/python/xen/xm/help.py | 82 ++++++++ tools/python/xen/xm/main.py | 25 +++ tools/python/xen/xm/opts.py | 18 +- 8 files changed, 208 insertions(+), 292 deletions(-) delete mode 100755 tools/python/xen/xend/server/cstruct.py create mode 100644 tools/python/xen/xm/help.py diff --git a/.rootkeys b/.rootkeys index 40c0fe962f..3cbef2d421 100644 --- a/.rootkeys +++ b/.rootkeys @@ -259,7 +259,6 @@ 40c9c469N2-b3GqpLHHHPZykJPLVvA tools/python/xen/xend/server/channel.py 40c9c469hJ_IlatRne-9QEa0-wlquw tools/python/xen/xend/server/console.py 40c9c469UcNJh_NuLU0ytorM0Lk5Ow tools/python/xen/xend/server/controller.py -40c9c469vHh-qLiiubdbKEQbJf18Zw tools/python/xen/xend/server/cstruct.py 40d83983OXjt-y3HjSCcuoPp9rzvmw tools/python/xen/xend/server/domain.py 40c9c469yrm31i60pGKslTi2Zgpotg tools/python/xen/xend/server/messages.py 40c9c46925x-Rjb0Cv2f1-l2jZrPYg tools/python/xen/xend/server/netif.py @@ -267,6 +266,7 @@ 40c9c469LNxLVizOUpOjEaTKKCm8Aw tools/python/xen/xend/sxp.py 40d05079aFRp6NQdo5wIh5Ly31c0cg tools/python/xen/xm/__init__.py 40cf2937gKQcATgXKGtNeWb1PDH5nA tools/python/xen/xm/create.py +40e41cd2w0I4En6qrJn4em8HkK_oxQ tools/python/xen/xm/help.py 40cf2937isyS250zyd0Q2GuEDoNXfQ tools/python/xen/xm/main.py 40cf2937PSslwBliN1g7ofDy2H_RhA tools/python/xen/xm/opts.py 40cf2937Z8WCNOnO2FcWdubvEAF9QQ tools/python/xen/xm/shutdown.py diff --git a/tools/examples/xmdefaults b/tools/examples/xmdefaults index f7b2f53c21..cf9e9861db 100644 --- a/tools/examples/xmdefaults +++ b/tools/examples/xmdefaults @@ -2,17 +2,24 @@ #============================================================================ # Python defaults setup for 'xm create'. # Edit this file to reflect the configuration of your system. -# This file expects the variable 'vmid' to be set. -#============================================================================ - -try: - vmid = int(vmid) # convert to integer -except: - raise ValueError, "Variable 'vmid' must be an integer" -if vmid <= 0: - raise ValueError, "Variable 'vmid' must be greater than 0" +#============================================================================ +# Define script variables here. +# xm_vars is defined automatically, use xm_vars.var() to define a variable. + +def vmid_check(var, val): + val = int(val) + if val <= 0: + raise ValueError + return val + +xm_vars.var('vmid', + use="Virtual machine id. Integer greater than 0.", + check=vmid_check) + +# This checks the script variables. +xm_vars.check() #---------------------------------------------------------------------------- # Kernel image file. diff --git a/tools/python/xen/xend/XendClient.py b/tools/python/xen/xend/XendClient.py index 13dc3dbb1e..12a21c2c9c 100644 --- a/tools/python/xen/xend/XendClient.py +++ b/tools/python/xen/xend/XendClient.py @@ -1,6 +1,11 @@ # Copyright (C) 2004 Mike Wray """Client API for the HTTP interface on xend. Callable as a script - see main(). + +This API is the 'control-plane' for xend. +The 'data-plane' is done separately. For example, consoles +are accessed via sockets on xend, but the list of consoles +is accessible via this API. """ import sys import httplib @@ -27,6 +32,8 @@ class Foo(httplib.HTTPResponse): def sxprio(sxpr): + """Convert an sxpr to a string. + """ io = StringIO() sxp.show(sxpr, out=io) print >> io @@ -80,6 +87,12 @@ def eventurl(location, root, id=''): return urljoin(location, root, 'event/', id) def xend_request(url, method, data=None): + """Make a request to xend. + + url xend request url + method http method: POST or GET + data request argument data (dict) + """ urlinfo = urlparse.urlparse(url) (uproto, ulocation, upath, uparam, uquery, ufrag) = urlinfo if DEBUG: print url, urlinfo @@ -122,20 +135,37 @@ def xend_request(url, method, data=None): return val def xend_get(url, args=None): + """Make a xend request using GET. + Requests using GET are 'safe' and may be repeated without + nasty side-effects. + """ return xend_request(url, "GET", args) def xend_call(url, data): + """Make xend request using POST. + Requests using POST potentially cause side-effects and should + not be repeated unless it really is wanted to do the side + effect again. + """ return xend_request(url, "POST", data) class Xend: + """Default location of the xend server.""" SRV_DEFAULT = "localhost:8000" + + """Default path to the xend root on the server.""" ROOT_DEFAULT = "/xend/" def __init__(self, srv=None, root=None): self.bind(srv, root) def bind(self, srv=None, root=None): + """Bind to a given server. + + srv server location (host:port) + root server xend root path + """ if srv is None: srv = self.SRV_DEFAULT if root is None: root = self.ROOT_DEFAULT if not root.endswith('/'): root += '/' diff --git a/tools/python/xen/xend/server/cstruct.py b/tools/python/xen/xend/server/cstruct.py deleted file mode 100755 index 880931b41f..0000000000 --- a/tools/python/xen/xend/server/cstruct.py +++ /dev/null @@ -1,269 +0,0 @@ -import struct - -class Struct: - - maxDepth = 10 - - base = ['x', 'B', 'H', 'I', 'L', 'Q', 'c', 'h', 'i', 'l', 'q', ] - - sizes = {'B': 1, - 'H': 2, - 'I': 4, - 'L': 4, - 'Q': 8, - 'c': 1, - 'h': 2, - 'i': 4, - 'l': 4, - 'q': 8, - 'x': 1, - } - - formats = { - 'int8' : 'B', - 'int16' : 'H', - 'int32' : 'I', - 'int64' : 'Q', - 'u8' : 'B', - 'u16' : 'H', - 'u32' : 'I', - 'u64' : 'Q' - } - - def typedef(self, name, val): - self.formats[name] = val - - def struct(self, name, *f): - self.typedef(name, StructInfo(self, f)) - - def getType(self, name): - return self.formats[name] - - def format(self, ty): - d = 0 - f = ty - while d < self.maxDepth: - d += 1 - f = self.formats[f] - if isinstance(f, StructInfo): - return f.format() - if f in self.base: - return f - return -1 - - def alignedformat(self, ty): - fmt = self.format(ty) - #print 'alignedformat> %s |%s|' %(ty, fmt) - afmt = self.align(fmt) - #print 'alignedformat< %s |%s| |%s|' % (ty, fmt, afmt) - return afmt - - def align(self, fmt): - n1 = 0 - afmt = '' - for a in fmt: - n2 = self.getSize(a) - m = n1 % n2 - if m: - d = (n2 - m) - afmt += 'x' * d - n1 += d - afmt += a - n1 += n2 - return afmt - - def fmtsize(self, fmt): - s = 0 - for f in fmt: - s += self.getSize(f) - return s - - def getSize(self, f): - return self.sizes[f] - - def pack(self, ty, data): - return self.getType(ty).pack(data) - - def unpack(self, ty, data): - return self.getType(ty).unpack(data) - - def show(self): - l = self.formats.keys() - l.sort() - for v in l: - print "%-35s %-10s %s" % (v, self.format(v), self.alignedformat(v)) - - -class StructInfo: - - def __init__(self, s, f): - self.fmt = None - self.structs = s - self.fields = f - - def alignedformat(self): - if self.afmt: return self.afmt - self.afmt = self.structs.align(self.format()) - return self.afmt - - def format(self): - if self.fmt: return self.fmt - fmt = "" - for (ty, name) in self.fields: - fmt += self.formatString(ty) - self.fmt = fmt - return fmt - - def formatString(self, ty): - if ty in self.fields: - ty = self.fields[ty] - return self.structs.format(ty) - - def pack(self, *args): - return struct.pack(self.alignedformat(), *args) - - def unpack(self, data): - return struct.unpack(self.alignedformat(), data) - -types = Struct() - -types.typedef('short' , 'h') -types.typedef('int' , 'i') -types.typedef('long' , 'l') -types.typedef('unsigned short', 'H') -types.typedef('unsigned int' , 'I') -types.typedef('unsigned long' , 'L') -types.typedef('domid_t' , 'u64') -types.typedef('blkif_vdev_t' , 'u16') -types.typedef('blkif_pdev_t' , 'u16') -types.typedef('blkif_sector_t', 'u64') - -types.struct('u8[6]', - ('u8', 'a1'), - ('u8', 'a2'), - ('u8', 'a3'), - ('u8', 'a4'), - ('u8', 'a5'), - ('u8', 'a6')) - -types.struct('blkif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn')) - -types.struct('blkif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('blkif_fe_interface_connect_t', - ('unsigned int' , 'handle'), - ('unsigned long', 'shmem_frame')) - -types.struct('blkif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('blkif_extent_t', - ('blkif_pdev_t' , 'device'), - ('blkif_sector_t', 'sector_start'), - ('blkif_sector_t', 'sector_length')) - -types.struct('blkif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'blkif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('blkif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_create_t', - ('domid_t' , 'domid'), #Q - ('unsigned int', 'blkif_handle'), #I - ('blkif_vdev_t', 'vdevice'), #H - ('int' , 'readonly'), #i - ('unsigned int', 'status')) #I - -types.struct('blkif_be_vbd_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_vbd_grow_t', - ('domid_t' , 'domid'), #Q - ('unsigned int' , 'blkif_handle'), #I - ('blkif_vdev_t' , 'vdevice'), #H - ('blkif_extent_t', 'extent'), #HQQ - ('unsigned int' , 'status')) #I - -types.struct('blkif_be_vbd_shrink_t', - ('domid_t' , 'domid'), - ('unsigned int', 'blkif_handle'), - ('blkif_vdev_t', 'vdevice'), - ('unsigned int', 'status')) - -types.struct('blkif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_status_changed_t', - ('unsigned int', 'handle'), - ('unsigned int', 'status'), - ('unsigned int', 'evtchn'), - ('u8[6]', 'mac')) - -types.struct('netif_fe_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -types.struct('netif_fe_interface_connect_t', - ('unsigned int', 'handle'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame')) - -types.struct('netif_fe_interface_disconnect_t', - ('unsigned int', 'handle')) - -types.struct('netif_be_create_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('u8[6]' , 'mac'), - ('unsigned int', 'status')) - -types.struct('netif_be_destroy_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_connect_t', - ('domid_t' , 'domid'), - ('unsigned int' , 'netif_handle'), - ('unsigned int' , 'evtchn'), - ('unsigned long', 'tx_shmem_frame'), - ('unsigned long', 'rx_shmem_frame'), - ('unsigned int' , 'status')) - -types.struct('netif_be_disconnect_t', - ('domid_t' , 'domid'), - ('unsigned int', 'netif_handle'), - ('unsigned int', 'status')) - -types.struct('netif_be_driver_status_changed_t', - ('unsigned int', 'status'), - ('unsigned int', 'nr_interfaces')) - -if 1 or __name__ == "__main__": - types.show() diff --git a/tools/python/xen/xm/create.py b/tools/python/xen/xm/create.py index 7819243adb..36bcddef92 100644 --- a/tools/python/xen/xm/create.py +++ b/tools/python/xen/xm/create.py @@ -1,4 +1,5 @@ # Copyright (C) 2004 Mike Wray + """Domain creation. """ import string @@ -15,12 +16,22 @@ from xen.xm.opts import * gopts = Opts(use="""[options] Create a domain. + +Domain creation parameters can be set by command-line switches, from +a python configuration script or an SXP config file. See documentation +for --defaults, --config. Configuration variables can be set using +VAR=VAL on the command line. For example vmid=3 sets vmid to 3. + """) gopts.opt('help', short='h', fn=set_true, default=0, use="Print this help.") +gopts.opt('help_config', + fn=set_true, default=0, + use="Print help for configuration file.") + gopts.opt('quiet', short='q', fn=set_true, default=0, use="Quiet.") @@ -31,24 +42,40 @@ gopts.opt('path', val='PATH', gopts.opt('defaults', short='f', val='FILE', fn=set_value, default='xmdefaults', - use="Use the given default script.") + use="""Use the given Python defaults script. +The defaults script is loaded after arguments have been processed. +Each command-line option sets a configuration variable named after +its long option name, and these variables are placed in the +environment of the script before it is loaded. +Variables for options that may be repeated have list values. +Other variables can be set using VAR=VAL on the command line. + +After the script is loaded, option values that were not set on the +command line are replaced by the values set in the script. +""") gopts.opt('config', short='F', val='FILE', fn=set_value, default=None, - use='Domain configuration to use (SXP).') + use="""Domain configuration to use (SXP). +SXP is the underlying configuration format used by Xen. +SXP configs can be hand-written or generated from Python defaults +scripts, using the -n (dryrun) option to print the config. +""") gopts.opt('load', short='L', val='FILE', fn=set_value, default=None, use='Domain saved state to load.') -gopts.opt('define', short='D', val='VAR=VAL', - fn=set_var, default=None, - use="""Set a variable before loading defaults, e.g. '-D vmid=3' - to set vmid. May be repeated to set more thanone variable.""") +#gopts.opt('define', short='D', val='VAR=VAL', +# fn=set_var, default=None, +# use="""Set a variable before loading defaults, e.g. '-D vmid=3' +# to set vmid. May be repeated to set more than one variable.""") gopts.opt('dryrun', short='n', fn=set_true, default=0, - use="Dry run - print the config but don't create the domain.") + use="""Dry run - print the config but don't create the domain. +The defaults file is loaded and the SXP configuration is created and printed. +""") gopts.opt('name', short='N', val='NAME', fn=set_value, default=None, @@ -352,7 +379,15 @@ def main(argv): args = opts.parse(argv) if opts.vals.help: opts.usage() + if opts.vals.help or opts.vals.help_config: + opts.load_defaults(help=1) + if opts.vals.help or opts.vals.help_config: return + # Process remaining args as config variables. + for arg in args: + if '=' in arg: + (var, val) = arg.strip().split('=') + gopts.setvar(var.strip(), val.strip()) if opts.vals.config: pass else: diff --git a/tools/python/xen/xm/help.py b/tools/python/xen/xm/help.py new file mode 100644 index 0000000000..88df4412c0 --- /dev/null +++ b/tools/python/xen/xm/help.py @@ -0,0 +1,82 @@ +# Copyright (C) 2004 Mike Wray + +"""Variable definition and help support for Python defaults files. +""" + +import sys + +class Vars: + """A set of configuration variables. + """ + + def __init__(self, name, help, env): + """Create a variable set. + + name name of the defaults file + help help flag + env local environment + """ + self.name = name + self.help = help + self.env = env + self.vars = [] + + def var(self, name, use=None, check=None): + """Define a configuration variable. + If provided, the check function will be called as check(var, val) + where var is the variable name and val is its value (string). + It should return a new value for the variable, or raise ValueError if + the value is not acceptable. + + name variable name + use variable usage string + check variable check function + """ + self.vars.append(Var(name, use, check)) + + def check(self): + """Execute the variable checks or print help, depending on the value + of the help flag passed to the constructor. + """ + if self.help: + self.doHelp() + else: + for v in self.vars: + v.doCheck(self.env) + + def doHelp(self, out=sys.stderr): + """Print help for the variables. + """ + if self.vars: + print >>out, "\nConfiguration variables for %s:\n" % self.name + for v in self.vars: + v.doHelp(out) + print >>out + +class Var: + """A single variable. + """ + + def __init__(self, name, use, check): + """Create a variable. + + name variable name + use variable use string + check variable value check function + """ + self.name = name + self.use = use or '' + self.check = check + + def doCheck(self, env): + """Execute the check and set the variable to the new value. + """ + if not self.check: return + env[self.name] = self.check(self.name, env.get(self.name)) + + def doHelp(self, out): + """Print help for the variable. + """ + print >>out, "%-12s" % self.name, self.use + + diff --git a/tools/python/xen/xm/main.py b/tools/python/xen/xm/main.py index 3ab5d23cf7..97a36f6711 100644 --- a/tools/python/xen/xm/main.py +++ b/tools/python/xen/xm/main.py @@ -5,10 +5,12 @@ import os import os.path import sys from getopt import getopt +import socket from xen.xend import PrettyPrint from xen.xend import sxp from xen.xend.XendClient import server +from xen.xend.XendClient import main as xend_client_main from xen.xm import create, shutdown class Prog: @@ -65,6 +67,13 @@ class Xm: sys.exit(1) def main(self, args): + try: + self.main_call(args) + except socket.error, ex: + print >>sys.stderr, ex + self.err("Error connecting to xend, is xend running?") + + def main_call(self, args): """Main entry point. Dispatches to the progs. """ self.name = args[0] @@ -444,5 +453,21 @@ class ProgConsole(Prog): xm.prog(ProgConsole) +class ProgCall(Prog): + name = "call" + info = "Call xend api functions." + + def help (self, args): + print "call fn argss..." + print """ + Call a xend HTTP API function. The leading 'xend_' on the function +can be omitted. See xen.xend.XendClient for the API functions. +""" + + def main(self, args): + xend_client_main(args) + +xm.prog(ProgCall) + def main(args): xm.main(args) diff --git a/tools/python/xen/xm/opts.py b/tools/python/xen/xm/opts.py index eb07936f1e..25f11abad2 100644 --- a/tools/python/xen/xm/opts.py +++ b/tools/python/xen/xm/opts.py @@ -259,7 +259,7 @@ class Opts: for opt in self.options: opt.show() - def load_defaults(self): + def load_defaults(self, help=0): """Load a defaults script. Assumes these options set: 'path' search path 'default' script name @@ -270,12 +270,12 @@ class Opts: else: p = self.vals.defaults if os.path.exists(p): - self.load(p) + self.load(p, help) break else: self.err("Cannot open defaults file %s" % self.vals.defaults) - def load(self, defaults, help=0): + def load(self, defaults, help): """Load a defaults file. Local variables in the file are used to set options with the same names. Variables are not used to set options that are already specified. @@ -290,11 +290,17 @@ class Opts: cmd = '\n'.join(["import sys", "import os", "import os.path", - "import xen.util.ip", + "from xen.xm.help import Vars", + "from xen.util import ip", "xm_file = '%s'" % defaults, - "xm_help = %d" % help ]) + "xm_help = %d" % help, + "xm_vars = Vars(xm_file, xm_help, locals())", + ]) exec cmd in globals, locals - execfile(defaults, globals, locals) + try: + execfile(defaults, globals, locals) + except: + if not help: raise if help: return # Extract the values set by the script and set the corresponding # options, if not set on the command line. -- 2.30.2